home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 4: GNU Archives / Linux Cubed Series 4 - GNU Archives.iso / gnu / fontutil.6 / fontutil / fontutils-0.6 / bzrto / psutil.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-10-19  |  11.3 KB  |  350 lines

  1. /* psutil.c: utility routines for PostScript output.
  2.  
  3. Copyright (C) 1992 Free Software Foundation, Inc.
  4.  
  5. This program is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2, or (at your option)
  8. any later version.
  9.  
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. GNU General Public License for more details.
  14.  
  15. You should have received a copy of the GNU General Public License
  16. along with this program; if not, write to the Free Software
  17. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  18.  
  19. #include "config.h"
  20.  
  21. #include "encoding.h"
  22. #include "libfile.h"
  23.  
  24. #include "main.h"
  25. #include "psutil.h"
  26.  
  27. extern string version_string;
  28.  
  29.  
  30. /* Information for the PostScript FontInfo dictionary, etc.
  31.    (-ps-font-info)  */
  32. string ps_global_info = NULL;
  33.  
  34.  
  35. /* This should be used for outputting a string S on a line by itself.  */
  36. #define OUT_LINE(s) do { fprintf (f, "%s\n", s); } while (0)
  37.  
  38. /* These output their arguments, preceded by the indentation.  */
  39. #define OUT1(s, e) do { fprintf (f, s, e); } while (0)
  40. #define OUT2(s, e1, e2) do { fprintf (f, s, e1, e2); } while (0)
  41.  
  42. static string option_value (string, string);
  43. static void out_notdef_loop (FILE *, charcode_type, charcode_type);
  44.  
  45. /* Return the information that goes in the FontInfo dictionary, etc.  We
  46.    use the global variables `ps_global_info' and `tfm_global_info' to
  47.    determine the information.  Either or both might be null.  */
  48.  
  49. ps_font_info_type
  50. ps_set_font_info ()
  51. {
  52.   ps_font_info_type ret;
  53.  
  54.   /* The PostScript name of the font.  */
  55.   ret.font_name = option_value (ps_global_info, "FontName") ? : "unknown";
  56.   
  57.   /* The typeface family which includes this font.  */
  58.   ret.family_name = option_value (ps_global_info, "FamilyName");
  59.   if (!ret.family_name)
  60.     { /* Since they didn't specify a family name, try to guess from the
  61.          font name.  The family name is `Times' for the font
  62.          `Times-BoldItalic', but it's `Helvetica' for `Helvetica'.  (It
  63.          should be `Lucida' for `LucidaBright-Italic', but we don't
  64.          handle that case.)  */
  65.       ret.family_name = strchr (ret.font_name, '-');
  66.       if (!ret.family_name)
  67.         ret.family_name = ret.font_name;  /* The `Helvetica' case.  */
  68.       else
  69.         ret.family_name
  70.           = substring (ret.font_name, 0, ret.family_name - 1 - ret.font_name);
  71.     }
  72.  
  73.   ret.weight = option_value (ps_global_info, "Weight");
  74.   if (!ret.weight)
  75.     { /* Again, since they didn't specify a weight, try to guess from
  76.          the font name.  */
  77.       unsigned w;
  78.       string weights[]
  79.         = { "Black", "Book", "Bold", "Demi", "ExtraBold", "Light",
  80.             "Heavy", "Regular", "Semibold", "Ultra", NULL };
  81.       
  82.       for (w = 0; weights[w] != NULL && !strstr (ret.font_name, weights[w]);
  83.            w++)
  84.         ;
  85.       ret.weight = weights[w] ? : "Medium";
  86.     }
  87.  
  88.   /* We should be able to compute the italic angle by somehow looking at
  89.      the characters.  bdftops.ps rotates the `I' and takes the angle
  90.      that minimizes the width, for example.  */
  91.   {
  92.     string angle_str = option_value (ps_global_info, "ItalicAngle");
  93.     ret.italic_angle = angle_str ? atof (angle_str) : 0;
  94.   }
  95.   
  96.   /* The interword space must come from `tfm_global_info'.  Otherwise,
  97.      we just let it be zero.  */
  98.   ret.interword_space
  99.     = tfm_global_info
  100.       ? TFM_SAFE_FONTDIMEN (*tfm_global_info, TFM_SPACE_PARAMETER, 0.0)
  101.       : 0.0;
  102.  
  103.   /* Monospaced fonts have no stretch or shrink in their interword
  104.      space (or shouldn't), but they do have a nonzero interword space.
  105.      (Except TeX math fonts have all their interword space parameters
  106.      set to zero).  */
  107.   {
  108.     string monospace_str = option_value (ps_global_info, "isFixedPitch");
  109.     if (monospace_str)
  110.       ret.monospace_p = STREQ (monospace_str, "true"); 
  111.     else if (tfm_global_info)
  112.       ret.monospace_p
  113.         =    TFM_SAFE_FONTDIMEN (*tfm_global_info, TFM_STRETCH_PARAMETER, -1)
  114.              == 0.0
  115.           && TFM_SAFE_FONTDIMEN (*tfm_global_info, TFM_SHRINK_PARAMETER, -1)
  116.              == 0.0
  117.           && TFM_SAFE_FONTDIMEN (*tfm_global_info, TFM_SPACE_PARAMETER, 0)
  118.              > 0.0;
  119.     else
  120.       ret.monospace_p = false;
  121.   }
  122.  
  123.   /* What might be a good way to compute this one?  */
  124.   {
  125.     string under_str = option_value (ps_global_info, "UnderlinePosition");
  126.     ret.underline_position = under_str ? atof (under_str) : -100;
  127.   }
  128.   
  129.   /* Here we use the rule thickness from the TFM file, if it's set.
  130.      Otherwise, we should guess the dominant stem width in the font, but
  131.      that's too much work for too little return.  */
  132.   {
  133. #define DEFAULT_THICKNESS 50
  134.     string under_str = option_value (ps_global_info, "UnderlineThickness");
  135.     if (under_str)
  136.       ret.underline_thickness = atof (under_str);
  137.     else if (tfm_global_info)
  138.       ret.underline_thickness
  139.         = TFM_SAFE_FONTDIMEN (*tfm_global_info,
  140.                               TFM_DEFAULTRULETHICKNESS_PARAMETER,
  141.                               DEFAULT_THICKNESS);
  142.     else
  143.       ret.underline_thickness = DEFAULT_THICKNESS;
  144.   }
  145.  
  146.   /* What to do about the UniqueID's?  Actually, I'm not sure they
  147.      should really be necessary.  Adobe wants people to register their
  148.      fonts to get a UniqueID from them, which is semi-reasonable, but a
  149.      lot of trouble.  We just omit them.  */
  150.   {
  151.     string unique_id_str = option_value (ps_global_info, "UniqueID");
  152.     ret.unique_id = unique_id_str ? atou (unique_id_str) : 0;
  153.     if (ret.unique_id >= 1 << 24)
  154.       {
  155.         WARNING1 ("%u: UniqueID >= 2**24; changing to zero", ret.unique_id);
  156.         ret.unique_id = 0;
  157.       }
  158.   }
  159.   
  160.   /* If there is no version number in the TFM file, then just say it's
  161.      version 0.  */
  162.   {
  163.     ret.version = option_value (ps_global_info, "version");
  164.     if (!ret.version)
  165.       ret.version
  166.         = tfm_global_info
  167.           && TFM_FONTDIMEN_COUNT (*tfm_global_info) >= TFM_VERSION_PARAMETER
  168.           ? dtoa (TFM_FONTDIMEN (*tfm_global_info, TFM_VERSION_PARAMETER))
  169.           : "0";
  170.   }
  171.  
  172.   return ret;
  173. }
  174.  
  175.  
  176. /* Consider OPTION_STR as a list of <name>:<value> items separated by
  177.    commas; return the <value> (as a string) associated with NAME, or
  178.    NULL.  This could plausibly be put in `lib', but since we have one
  179.    place (the routine just above) where we need to set each of a given
  180.    set of names, as opposed to doing something with each specified
  181.    item, we just define it here.  */
  182.  
  183. static string 
  184. option_value (string option_str, string name)
  185. {
  186.   string item_end;
  187.   string value = NULL;
  188.   string item_start = option_str;
  189.  
  190.   if (option_str == NULL)
  191.     return NULL;
  192.     
  193.   do
  194.     {
  195.       string item, colon;
  196.       
  197.       item_end = strchr (item_start, ',');
  198.       item = item_end
  199.              ? substring (item_start, 0, item_end - 1 - item_start)
  200.              : item_start;
  201.       colon = strchr (item, ':');
  202.       
  203.       if (colon)
  204.         {
  205.           string item_name = substring (item, 0, colon - 1 - item);
  206.           
  207.           if (STREQ (item_name, name))
  208.             value = xstrdup (colon + 1);
  209.  
  210.           free (item_name);
  211.         }
  212.       else
  213.         WARNING1 ("%s: Missing colon in option string", item);
  214.       
  215.       if (item != item_start)
  216.         free (item);
  217.       
  218.       item_start = item_end + 1;
  219.     }
  220.   while (item_end);
  221.   
  222.   return value;
  223. }
  224.  
  225. /* Output the common beginning (after the %! line) of a PostScript font
  226.    to F, using the other args for information.  */
  227.  
  228. void
  229. ps_start_font (FILE *f, ps_font_info_type ps_info, string comment)
  230. {
  231.   OUT1 ("%%%%Creator: %s\n", version_string);
  232.   OUT1 ("%%%%CreationDate: %s\n", now ());
  233.   OUT_LINE ("%%DocumentData: Clean7Bit");
  234.   OUT_LINE ("%%EndComments");
  235.   OUT1 ("%% %s\n", comment);
  236.   OUT_LINE ("% This font is in the public domain.");
  237.  
  238.   OUT1 ("/%s 11 dict begin\n", ps_info.font_name);
  239.   
  240.   /* The FontInfo dictionary, which has additional
  241.      (supposedly optional) information.  */
  242.   OUT_LINE ("/FontInfo 8 dict begin");
  243.   OUT1     ("  /version (%s) readonly def\n", ps_info.version);
  244.   OUT1     ("  /FullName (%s) readonly def\n", ps_info.font_name);
  245.   OUT1     ("  /FamilyName (%s) readonly def\n", ps_info.family_name);
  246.   OUT1     ("  /Weight (%s) readonly def\n", ps_info.weight);
  247.   OUT1     ("  /ItalicAngle %d def\n", ps_info.italic_angle);
  248.   OUT1     ("  /isFixedPitch %s def\n",
  249.                  ps_info.monospace_p ? "true" : "false");
  250.   OUT1     ("  /underlinePosition %d def\n", ps_info.underline_position);
  251.   OUT1     ("  /underlineThickness %d def\n", ps_info.underline_thickness);
  252.   OUT_LINE ("currentdict end readonly def");
  253.   
  254.   /* Other constant top-level elements of the font dictionary.  Assume
  255.      that the font name is the only thing on the stack at this point
  256.      (which is true).  This saves us from having to write the name
  257.      twice.  */
  258.   OUT_LINE ("/FontName 1 index def");
  259.   OUT_LINE ("/PaintType 0 def");  /* All our fonts are filled.  */
  260.  
  261.   if (ps_info.unique_id)
  262.     OUT1 ("/UniqueID %d def\n", ps_info.unique_id);
  263. }
  264.  
  265. /* Return the name corresponding to the number N in the current
  266.    encoding.  If the number is not defined, give a warning and return
  267.    ".notdef".  */
  268.  
  269. string
  270. ps_encoding_name (charcode_type n)
  271. {
  272.   string ret = ENCODING_CHAR_NAME (encoding_info, n);
  273.   if (ret == NULL)
  274.     {
  275.       WARNING2 ("%d: No such character number in encoding scheme `%s'",
  276.                 n, ENCODING_SCHEME_NAME (encoding_info));
  277.       ret = ".notdef";
  278.     }
  279.   return ret;
  280. }
  281.  
  282.  
  283. /* Return the number corresponding to NAME in the current encoding.  If
  284.    NAME is not defined, return -1.  */
  285.  
  286. int
  287. ps_encoding_number (string name)
  288. {
  289.   int ret = encoding_number (encoding_info, name);
  290.   return ret;
  291. }
  292.  
  293.  
  294. /* Output the encoding vector to the file F, based on CHAR_OUTPUT_P and
  295.    the global `encoding'.  */
  296.  
  297. void
  298. ps_output_encoding (FILE *f, boolean char_output_p[])
  299. {
  300.   unsigned c;
  301.   int range_start = -1;
  302.  
  303.   fprintf (f, "/Encoding %d array %% %s\n", ENCODING_VECTOR_SIZE,
  304.            ENCODING_SCHEME_NAME (encoding_info));
  305.  
  306.   for (c = 0; c < ENCODING_VECTOR_SIZE; c++)
  307.     { /* Write sequences of .notdef's and characters we didn't output
  308.          using a loop.  */
  309.       if (!char_output_p[c]
  310.           || ENCODING_CHAR_NAME (encoding_info, c) == NULL
  311.       || STREQ (ENCODING_CHAR_NAME (encoding_info, c), ".notdef"))
  312.     { /* Start or continue a loop.  */
  313.       if (range_start == -1)
  314.             range_start = c;
  315.     }
  316.       else
  317.     { /* Finish a loop if one is started, then output the current
  318.              character.  */
  319.           if (range_start != -1)
  320.             { /* Loop started, finish it.  */
  321.               out_notdef_loop (f, range_start, c - 1);
  322.               range_start = -1;
  323.             }
  324.           
  325.           /* Output the current character.  */
  326.           OUT2 ("  dup %d /%s put\n",c, ENCODING_CHAR_NAME (encoding_info, c));
  327.     }
  328.     }
  329.   
  330.   /* If the character 255 is not encoded, we'll be in the midst of a loop. */
  331.   if (range_start != -1)
  332.     out_notdef_loop (f, range_start, c - 1);
  333.   
  334.   /* Define it.  */
  335.   OUT_LINE ("  readonly def");
  336. }
  337.  
  338.  
  339. /* Output a loop to put .notdef's into the encoding vector between START
  340.    and END.  Assume the `Encoding' array is the top of the stack.  */
  341.  
  342. static void
  343. out_notdef_loop (FILE *f, charcode_type start, charcode_type end)
  344. {
  345.   if (start == end)
  346.     OUT1 ("  dup %d /.notdef put\n", start);
  347.   else
  348.     OUT2 ("  %d 1 %d { 1 index exch /.notdef put } bind for\n", start, end);
  349. }
  350.